#pragma once
#include <sys/select.h>
#include "sock.hpp"

namespace ns_selectserver
{
    using namespace ns_sock;
    const u_int16_t PORT = 8080;
    const int NUM (sizeof(fd_set) * 8);
    class SelectServer
    {
    private:
        u_int16_t _port;
        int _listen_sock;
        int max_fd;
        int _fd_arrays[NUM];
    public:
        SelectServer(u_int16_t port = PORT)
            :_port(port)
            ,_listen_sock(-1)
            ,max_fd(0)
        {
            for (int i = 0; i < NUM; ++i) {
                _fd_arrays[i] = -1;
            }
        }
        void InitServer() {
            _listen_sock = Sock::Socket();
            Sock::Bind(_listen_sock, _port);
            Sock::Listen(_listen_sock);
            _fd_arrays[0] = _listen_sock;
        }
        void HanderSock(const fd_set &rfds) {
            for (int i = 0; i < NUM; ++i) {
                if (-1 == _fd_arrays[i]) {
                    continue;
                }
                if (FD_ISSET(_fd_arrays[i], &rfds)) {
                    // 检测到就绪的文件描述符
                    if (_fd_arrays[i] == _listen_sock) {
                        // 有新链接
                        struct sockaddr_in peer;
                        socklen_t len = sizeof(peer);
                        int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
                        if (sock < 0) {
                            std::cout << "accept 文件描述符失败"  << std::endl;
                            continue;
                        } else {
                            int index = -1;
                            for (int j = 1; j < NUM; ++j) {
                                if (-1 == _fd_arrays[j]) {
                                    index = j;
                                    break;
                                }
                            }
                            if (-1 == index) {
                                std::cout << "_fd_array满了" << std::endl;
                                close(sock);
                            } else {
                                _fd_arrays[index] = sock;
                                std::cout << "获取新链接成功， sock： " << sock << std::endl;
                            }
                        }
                    } else {
                        // 有数据到来
                        char buffer[1024];
                        int s = recv(_fd_arrays[i], buffer, sizeof(buffer) - 1, 0);
                        if (s > 0) {
                            buffer[s] = 0;
                            std::cout << "client say# " << buffer << std::endl;
                        } else if (s == 0) {
                            // 对端关闭
                            int sock = _fd_arrays[i];
                            _fd_arrays[i] = -1;
                            std::cout << "client quit...sock: " << sock << std::endl;
                            close(sock);
                        } else {
                            std::cout << "读取失败" << std::endl; 
                        }
                    }
                }
            }
        }
        void Loop() {
            fd_set rfds;
            while (true) {
                FD_ZERO(&rfds);
                for (int i = 0; i < NUM; ++i) {
                    // 把已经存在文件描述符设置进rfds中
                    if (-1 == _fd_arrays[i]) continue;
                    FD_SET(_fd_arrays[i], &rfds);
                    if (max_fd < _fd_arrays[i]) {
                        max_fd = _fd_arrays[i];
                    }
                }
                struct timeval timeout = {2, 0};
                // timeout: nullptr代表阻塞等待至少有一个fd就绪，不为nullptr表示需要等待多少长时间，若在这个时间内没有fd就绪，就返回0，表示超时；
                // 若在此期间有fd就绪，则返回，且timeout时间变为距离设置的最长等待时间还剩多长时间；设置为0表示非阻塞等待，没有fd就绪就立即返回 
                 int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr);
                 switch (n)
                 {
                 case 0:
                    std::cout << "超时返回" << std::endl;
                    //  超时
                     break;
                  case -1:
                    std::cout << "出错返回" << std::endl;
                    // 出错
                     break;                
                 default:
                    // std::cout << "select成功" << std::endl;
                    HanderSock(rfds);
                     break;
                 }
            }
        }
        ~SelectServer() {
            if (_listen_sock >= 0) {
                close(_listen_sock);
            }
        }
    };
} // namespace ns_selectserver
